/**
 * promise 原理
 * 
 * 1. exector
 *    需要传递一个执行函数exector参数，用于执行何时进行状态改变的逻辑
 *    这个函数需要两个参数 resovle, reject 分别用于处理成功、失败的状态改变
 * 
 * 2. status
 *    status 状态
 *    仅可以通过内部的resolve、reject内置函数进行修改，且仅当status == 'pending'状态可以被修改，状态一旦确定就无法被再次修改
 *    resolve、reject都会接收一个参数，并分别保存在data和reason中
 * 
 * 3. then 方法
 *    then 方法主要用于监听成功或者失败的回调
 *    接收两个参数resovleHandle、和rejectHandle函数用于处理成功、失败时的回调
 *    每个then函数仅会执行其中一个函数 成功或者失败，主要由status来判断
 *    且resovleHandle、和rejectHandle会自动传入内部保存的对应的数据data和reason
 *    若执行器内的状态改变涉及异步操作时，then方法实际处于pending的状态
 *    异步处理
 *        此时我们如果希望在特定的时刻执行then方法传递的resovleHandle、和rejectHandle，我们则需要对他们进行缓存，并在希望调用的时候调用(resolve, reject)
 *        cache: {resolveHandle: [], rejectHandle: []}，由于可能会有多个then方法的监听，所以这两个cache是数组
 * 
 * 4. then 方法的链式调用
 *    1. 链式调用的核心是then方法返回一个新的promise，并将then方法内的主要逻辑放在新promise的exector内执行，这是为了后面的一个步骤做准备
 *    2. 新的promise的then会接收上一个promise的then方法resolveHandle和rejectHandle返回的值
 *    3. 处理then方法未传递回调函数的防错机制
 *    4. 仅当上一个promise函数或者then内部逻辑发生错误的时候会触发下一个then方法的reject回调
 *    5. 若then的回调返回的是一个VPromise实例，我们需要立刻执行该promise的then方法，并在对应的回调中调用我们新的promise的状态函数
 *    6. 内部代码执行错误捕获机制，为then方法和exector函数内进行try/catch机制来捕获错误
 * 
 * 5. then方法防止调用自身导致死循环
 * 
 * 
 * 
 * 
 * 遇到的问题：
 *    1. 若resolve和reject方法定义在原型上的话，会导致获取不到this的值，导致报错
 *    2. try/catch只能catch到执行过程中发生的错误，不能catch到外部回调执行的错误
 *    3. finally的reject回调为何不直接返回错误，直接返回只会被resolve接收，而不是reject，我使用throw无法将错误传递到下面，这是因为我们打印的是error.message，而error是字符串是无法获取message
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class VPromise {
  constructor(exector) {
    // promise状态
    this.status = PENDING
    // 成功数据
    this.data = undefined
    // 失败原因
    this.reason = undefined
    // 回调缓存3
    this.cache = {
      resovleHandle: [],
      rejectHandle: []
    }
    // 执行执行器
    try {
      exector(this.resolve, this.reject)
    } catch (error) {
      this.reject(error.message || error)
    }
  }

  // 成功时执行操作
  resolve = (v) => {
    let {
      status,
      cache
    } = this
    // 判断status状态
    if (status !== PENDING) return
    // 修改状态为成功
    this.status = FULFILLED
    // 存储成功
    this.data = v

    // 执行特殊的异步回调
    if (cache && cache.resovleHandle) {
      while (cache.resovleHandle.length > 0) {
        let handle = cache.resovleHandle.pop()
        handle && handle(this.data)
      }
    }
  }

  // 失败时执行操作
  reject = (v) => {
    let {
      status, cache
    } = this
    // 判断status状态
    if (status !== PENDING) return
    // 修改状态为失败
    this.status = REJECTED
    // 存储失败原因
    this.reason = v
    // 执行特殊的异步回调
    if (cache && cache.rejectHandle) {
      while (cache.rejectHandle.length > 0) {
        let handle = cache.rejectHandle.pop()
        handle && handle(this.reason)
      }
    }
  }

  // 设置回调缓存
  setCache(res, rej) {
    res && this.cache.resovleHandle.push(res)
    rej && this.cache.rejectHandle.push(rej)
  }

  // then方法监听成功或失败操作
  then(resovleHandle = v => v, rejectHandle = e => {
    throw e
  }) {
    let promise = new VPromise((res, rej) => {
      // then方法主要运行逻辑
      if (this.status === FULFILLED) {
        let value = resovleHandle(this.data)
        // 为了将value传递给then方法的回调，我们可以传递给res，间接传递给then
        // 执行res，不仅可以传递参数，还可以开启下一个promis的执行
        // res(value)
        asyncHandle(value, res, rej)
      } else if (this.status === REJECTED) {
        // console.log('error: ' + this.reason)
        let error = rejectHandle(this.reason)
        // 同上
        asyncHandle(error, res, rej)
      } else {
        // 异步有点不同
        this.setCache((data) => {
          try {
            let value = resovleHandle(data)
            asyncHandle(value, res, rej)
          } catch (error) {
            rej(error.message || error)
          }
        }, (reason) => {
          try {
            let error = rejectHandle(reason)
            asyncHandle(error, res, rej)
          } catch (error) {
            rej(error.message || error)
          }
        })
      }
    })
    return promise
  }

  // 并行操作all
  static all(arrary = []) {
    let result = []
    let count = 0
    return new VPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        count++
        if (count === arrary.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < arrary.length; i++) {
        let res = arrary[i]
        if (res instanceof VPromise) {
          // 是promise
          res.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值
          addData(i, res)
        }
      }
    })
  }
  // finally 方法，不管promise的状态是什么都会执行finally传递的函数，并且可以链式调用
  finally(fn) {
    // 若fn的返回值是promise，需要等待该promise执行完成在向下执行
    return this.then((data) => {
      return VPromise.resolve(fn()).then(() => data)
    }, (reason) => {
      return VPromise.resolve(fn()).then(() => {
        throw reason
      }, () => {
        throw reason
      })
    })
  }

  catch(reject) {
    return this.then(undefined, reject)
  }

  // resolve方法，返回一个新的状态为resolve方法
  static resolve(data) {
    // 判断是否是promise对象
    // 是直接返回
    if (data instanceof VPromise) return data
    // 否则返回一个状态为fulfilled的promise对象
    return new VPromise((resolve) => resolve(data))
  }

  // reject方法，返回一个新的状态为reject方法
  static reject(data) {
    // 判断是否是promise对象
    // 是直接返回
    if (data instanceof VPromise) return data
    // 否则返回一个状态为rejected的promise对象
    return new VPromise((resolve, reject) => reject(data))
  }


}

// 用于处理promise和非promise的情况
function asyncHandle(value, res, rej) {
  if (value instanceof VPromise) {
    // 说明是promise
    // 执行promise的then方法来判断何是执行res或reject
    // 并将then方法回调中的传入值作为下一个promise的参数值
    value.then((data) => {
      res(data)
    }, (err) => {
      rej(err)
    })
  } else {
    // 否则是普通值
    res(value)
  }
}

let promise = new VPromise((res, rej) => {
  // res(1)
  // rej('c023-error')
  // a()
  setTimeout(() => {
    // res('success')
    rej('fail...')
  }, 1000)
})

promise.finally(() => {
  return new VPromise((res, rej) => {
    // res(1)
    // rej('c023-error')
    // a()
    setTimeout(() => {
      // res('success')
      rej('fail.')
    }, 1000)
  })
}).then((v) => {
  console.log('success')
  console.log(v)
}, (err) => {
  console.log('err')
  console.log(err)
})

